#include <QUrl>
#include <QNetworkRequest>
#include <QSslConfiguration>
#include <QNetworkReply>
#include <QDomDocument>
#include <QThread>
#include "QJsonDocument"
#include "QJsonObject"
#include "ssloperation.h"

SslOperation::ContentParameter::ContentParameter():
    key(""),
    value(""),
    hash_key("")
{
}

SslOperation::ContentParameter::ContentParameter(QString _key, QString _value, QString _hash_key):
    key(_key),
    value(_value),
    hash_key(_hash_key)
{
}

SslOperation::ContentParameter& SslOperation::ContentParameter::operator=(const ContentParameter &other)
{
    key=other.key;
    value=other.value;
    hash_key=other.hash_key;
    return *this;
}

bool SslOperation::ContentParameter::operator==(const ContentParameter &other)
{
    if((key==other.key)&&(value==other.value)&&(hash_key==other.hash_key)) return true;
    else return false;
}

SslOperation::OperationContetnt::OperationContetnt():
    address(""),
    method(NONE),
    errorString(""),
    ReceiveParamenters(QList<ContentParameter>()),
    SendParamenters(QList<ContentParameter>())
{
}

SslOperation::OperationContetnt::OperationContetnt(Method _method, QString _address,
                                                   QList<ContentParameter> _ReceiveParamenters,
                                                   QList<ContentParameter> _SendParamenters,
                                                   ContentAnswerType _answerType,
                                                   ContentRequstType _requestType, QString _errorString):
    address(_address),
    method(_method),
    errorString(_errorString),
    ReceiveParamenters(_ReceiveParamenters),
    SendParamenters(_SendParamenters),
    answerType(_answerType),
    requestType(_requestType)
{
}

SslOperation::OperationContetnt& SslOperation::OperationContetnt::operator=(const OperationContetnt &other)
{
    address=other.address;
    method=other.method;
    errorString=other.errorString;
    ReceiveParamenters.clear();
    ReceiveParamenters=other.ReceiveParamenters;
    SendParamenters.clear();
    SendParamenters=other.SendParamenters;
    return *this;
}

bool SslOperation::OperationContetnt::operator==(const OperationContetnt &other)
{
    if((!address.compare(other.address))&&
       (method==other.method)&&
       (!errorString.compare(other.errorString))&&
       (ReceiveParamenters==other.ReceiveParamenters)&&
       (SendParamenters==other.SendParamenters))
        return true;
    else
        return false;
}



SslOperation::SslOperation(QList<OperationContetnt> *operList, QObject *parent):
    QObject(parent),
    m_operList(operList),
    m_currentOperation(0),
    m_finishedOperation(true)
{
//    m_socket->setParent(this);
    //m_manager = new QNetworkAccessManager;
    connect(this, SIGNAL(OperFinished()), this, SLOT(finishedOper()));
}

SslOperation::SslOperation(QObject *parent):
   QObject(parent),
   m_currentOperation(0),
   m_finishedOperation(true)
{
    //m_manager = new QNetworkAccessManager;
    connect(this, SIGNAL(OperFinished()), this, SLOT(finishedOper()));
}

SslOperation::~SslOperation()
{

}

void SslOperation::Engine()
{
    while(m_currentOperation<m_operList->size())
    {
        if(m_finishedOperation)
        {
            m_finishedOperation=false;
            switch(m_operList->at(m_currentOperation).method)
            {
            case CONNECT:
                    DoCONNECT();
                break;
            case GET:
                    DoGET();
                break;
            case POST:
                    DoPOST();
                break;
            case CLOSE:
                    DoCLOSE();
                break;
            };
        }
//        m_currentOperation++;
    }

}

void SslOperation::DoCONNECT()
{
    qDebug()<<"SslOperation::DoCONNECT id: "<<QThread::currentThreadId();
//    m_socket =  new QSslSocket;

//    connect(m_socket, SIGNAL(encrypted()), this, SLOT(Encrypted()));
//    connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
//            this, SLOT(socketError(QAbstractSocket::SocketError)));
//    connect(m_socket, SIGNAL(sslErrors(QList<QSslError>)),
//            this, SLOT(SslSocketError(QList<QSslError>)));

//    m_protocol=QSsl::TlsV1_0;
//    m_socket->setProtocol(m_protocol);
//    m_socket->setSocketOption(QAbstractSocket::KeepAliveOption,1);

//    m_socket->connectToHostEncrypted(TakeAddress(m_operList->at(m_currentOperation).address), 443);

    //m_manager = new QNetworkAccessManager;

    connect(&m_manager, SIGNAL(encrypted(QNetworkReply*)),
             this, SLOT(Encrypted(QNetworkReply*)));
    connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
            this, SLOT(sslErrorsSlot(QNetworkReply*,QList<QSslError>)));

    QUrl url(TakeAddress(m_operList->at(m_currentOperation).address));
    QNetworkRequest request;
    QSslConfiguration ssl_config;
    ssl_config.setProtocol(m_protocol);
    request.setSslConfiguration(ssl_config);
    request.setUrl(url);

    //m_manager.moveToThread(this->thread());
    m_manager.connectToHostEncrypted(TakeAddress(m_operList->at(m_currentOperation).address));
    //m_manager.moveToThread(this->thread());
}

void SslOperation::DoGET()
{
    //m_manager = new QNetworkAccessManager;

    connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
             this, SLOT(finishedGetRequestSlot(QNetworkReply*)));
    connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
            this, SLOT(sslErrorsSlot(QNetworkReply*,QList<QSslError>)));

    QUrl url(TakeAddress(m_operList->at(m_currentOperation).address));
    QNetworkRequest request;
    QSslConfiguration ssl_config;
    ssl_config.setProtocol(m_protocol);
    request.setSslConfiguration(ssl_config);
    request.setUrl(url);

    qDebug()<<"GET to "<<request.url().toString();
    m_manager.get(request);
}

void SslOperation::DoPOST()
{

    QNetworkRequest request;
    QSslConfiguration ssl_config;
    //m_manager = new QNetworkAccessManager;
    ssl_config.setProtocol(m_protocol);
    request.setSslConfiguration(ssl_config);
    request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
    request.setUrl(TakeAddress(m_operList->at(m_currentOperation).address));

    connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
            this, SLOT(sslErrorsSlot(QNetworkReply*,QList<QSslError>)));
    connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(finishedPostRequestSlot(QNetworkReply*)));

    if(m_operList->at(m_currentOperation).requestType==PercentEncoding)
    {
        QByteArray postData;
        postData.append(QUrl::toPercentEncoding((m_operList->at(m_currentOperation).SendParamenters.at(0).key.toUtf8())+"="+
                         QUrl::toPercentEncoding(m_operList->at(m_currentOperation).SendParamenters.at(0).value.toUtf8())));
        for(int i=1;i<m_operList->size();i++)
        {
            postData.append("&"+QUrl::toPercentEncoding((m_operList->at(m_currentOperation).SendParamenters.at(i).key.toUtf8())+"="+
                             QUrl::toPercentEncoding(m_operList->at(m_currentOperation).SendParamenters.at(i).value.toUtf8())));
        }
        qDebug()<<"postData: "<<postData;
        qDebug()<<"POST to "<<request.url().toString();
        m_manager.post(request, QString(postData).toUtf8());
    }
    else if(m_operList->at(m_currentOperation).requestType==JSONRequest)//!!
    {
        QJsonDocument *jsonRequest=new QJsonDocument;
        QJsonObject jsonObj;
        for(int i=0;i<m_operList->size();i++)
        {
           jsonObj[m_operList->at(m_currentOperation).SendParamenters.at(i).key]=m_operList->at(m_currentOperation).SendParamenters.at(i).value;
           jsonRequest->setObject(jsonObj);
        }
        QByteArray postData=jsonRequest->toJson(QJsonDocument::Compact);
        m_manager.post(request, QString(postData).toUtf8());
    }
}

void SslOperation::DoCLOSE()
{
    m_socket->close();
    emit OperFinished();
}

void SslOperation::SetOperList(QList<OperationContetnt> *operList)
{
    m_operList=operList;
    m_currentOperation=0;
    m_finishedOperation=true;
}

void SslOperation::SetManager(QNetworkAccessManager   *manager)
{
    //m_manager=*manager;
}

void SslOperation::Encrypted(QNetworkReply *reply)
{
    qDebug()<<"Encrypted";
    emit OperFinished();
}

void SslOperation::socketError(QAbstractSocket::SocketError)
{
    m_currentOperation==m_operList->size();
    qDebug()<<"SslOperation::socketError :"<<m_socket->errorString();
    emit Error(m_socket->errorString());
}

void SslOperation::SslSocketError(QList<QSslError> err_list)
{
    m_currentOperation==m_operList->size();
    qDebug()<<"SslOperation::SSL Socket Errors";
    emit Errors(err_list);
}

void SslOperation::sslErrorsSlot(QNetworkReply * reply, const QList<QSslError> &errors)
{
   m_currentOperation==m_operList->size();
   qDebug()<<"SslOperation::sslErrorsSlot Socket Errors";
   emit Errors(errors);
}

void SslOperation::finishedGetRequestSlot(QNetworkReply* reply)
{
    QObject::disconnect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(finishedGetRequestSlot(QNetworkReply*)));

    if (reply->error() == QNetworkReply::NoError)
    {
        QByteArray bytes = reply->readAll();  // bytes
        qDebug()<<"\n\nfinishedGETRequestSlot from :"<<reply->header(QNetworkRequest::LocationHeader).toString()<< "\n"<<bytes;

        QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        if((statusCodeV==301)||(statusCodeV==302))
        {
            QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
            qDebug()<<"redirectionTargetUrl="<<QString(redirectionTargetUrl.toString());
            QUrl url(redirectionTargetUrl.toString());
            QNetworkRequest request;
            QSslConfiguration ssl_config;
            ssl_config.setProtocol(m_protocol);
            request.setSslConfiguration(ssl_config);
            request.setUrl(url);

            //m_manager=new QNetworkAccessManager;
            QObject::connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(finishedGetRequestSlot2(QNetworkReply*)));
            qDebug()<<"GET to "<<request.url().toString();
            m_manager.get(request);
        }
        else
        {
            qDebug()<<"parsing host answer...";
            ParseAnswer(bytes);
            emit OperFinished();
        }
    }
    else
    {// Some http error received
        qDebug()<<"Get replyError: " << reply->error()<<": "<<reply->errorString();
        emit Error(reply->errorString());
    }
}

void SslOperation::finishedGetRequestSlot2(QNetworkReply* reply)
{
    QObject::disconnect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(finishedGetRequestSlot(QNetworkReply*)));

    if (reply->error() == QNetworkReply::NoError)
    {
        QByteArray bytes = reply->readAll();  // bytes
        qDebug()<<"\n\nfinishedGETRequestSlot from :"<<reply->header(QNetworkRequest::LocationHeader).toString()<< "\n"<<bytes;

        QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        if((statusCodeV==301)||(statusCodeV==302))
        {
            QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
            qDebug()<<"redirectionTargetUrl="<<QString(redirectionTargetUrl.toString());
            QUrl url(redirectionTargetUrl.toString());
            QNetworkRequest request;
            QSslConfiguration ssl_config;
            ssl_config.setProtocol(m_protocol);
            request.setSslConfiguration(ssl_config);
            request.setUrl(url);

            //m_manager=new QNetworkAccessManager;
            QObject::connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(finishedGetRequestSlot(QNetworkReply*)));
            qDebug()<<"GET to "<<request.url().toString();
            m_manager.get(request);
        }
        else
        {
            qDebug()<<"parsing host answer...";
            ParseAnswer(bytes);
            emit OperFinished();
        }
    }
    else
    {// Some http error received
        qDebug()<<"Get replyError: " << reply->error()<<": "<<reply->errorString();
        emit Error(reply->errorString());
    }
}

void SslOperation::finishedPostRequestSlot(QNetworkReply *reply)
{
    // no error received?
    if (reply->error() == QNetworkReply::NoError)
    {
        QByteArray replyBa=reply->readAll();
        qDebug()<<"\n\nfinishedPOSTRequestSlot from :"<<reply->header(QNetworkRequest::LocationHeader).toString()<< "\n"<<replyBa;

        QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        if((statusCodeV==301)||(statusCodeV==302))
        {
            QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
            qDebug()<<"redirectionTargetUrl="<<QString(redirectionTargetUrl.toString());
            QUrl url(redirectionTargetUrl.toString());
            QNetworkRequest request;
            QSslConfiguration ssl_config;
            ssl_config.setProtocol(m_protocol);
            request.setSslConfiguration(ssl_config);
            request.setUrl(url);
            //m_manager=new QNetworkAccessManager;
            connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
                    this, SLOT(sslErrorsSlot(QNetworkReply*,QList<QSslError>)));
            connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(finishedPostRequestSlot(QNetworkReply*)));
            connect(this, SIGNAL(OperFinished()), this, SLOT(finishedOper()));

            qDebug()<<"GET to "<<request.url().toString();
            m_manager.get(request);
        }
        else
        {
            qDebug()<<"parsing host answer...";
            ParseAnswer(replyBa);
            emit OperFinished();
        }
    }
    // Some http error received
    else
    {
        qDebug()<<"Post replyError: "<<reply->errorString();
        emit Error(reply->errorString());
    }

    reply->deleteLater();
}

void SslOperation::finishedOper()
{
    m_currentOperation++;
    m_finishedOperation=true;
    if(m_currentOperation>=m_operList->size())
        JobFinished();
}

void SslOperation::ParseAnswer(QByteArray &bytes)
{

    QList<ContentParameter> *recParam=(QList<ContentParameter> *)&(m_operList->at(m_currentOperation).ReceiveParamenters);
    if(m_operList->at(m_currentOperation).answerType==HTML)
    {
        //name="PaRes" value=" //call with ""PaRes" value=""
        QString ReplyStr(bytes); // string
        if((m_operList->at(m_currentOperation).errorString!="")&&(ReplyStr.indexOf(m_operList->at(m_currentOperation).errorString)>=0))
        {
            emit Error("Error in host answer");
        }
        else
        {
            for(int i=0;i<recParam->size();i++)
            {
                int start_index=ReplyStr.indexOf(recParam->at(i).key);
                if(start_index>=0) start_index+=recParam->at(i).key.length();
                    int last_index=-1;
                    if(recParam->at(i).value=="") last_index=ReplyStr.indexOf("\"",start_index);
                    else last_index=ReplyStr.indexOf(recParam->at(i).value,start_index);
                    if(last_index>=0)
                    {
                        recParam->value(i).value=ReplyStr.mid(start_index,last_index-start_index);
                        m_hash[recParam->at(i).hash_key]=recParam->at(i).value;
                    }
            }
        }
    }
    else if(m_operList->at(m_currentOperation).answerType==JSONAnswer)
    {
        QJsonDocument *jsonAnswer=new QJsonDocument;
        *jsonAnswer= QJsonDocument::fromJson(bytes) ;
        QJsonObject jsonObj=jsonAnswer->object();

        if(jsonObj.value(m_operList->at(m_currentOperation).errorString).isString())
        {
             emit Error(jsonObj.value(m_operList->at(m_currentOperation).errorString).toString());
        }
        else
        {
            for(int i=0;i<m_operList->at(m_currentOperation).ReceiveParamenters.size();i++)
            {
                if(jsonObj.value(recParam->at(i).key).isString())
                    m_hash[recParam->at(i).hash_key]=jsonObj.value(recParam->at(i).key).toString();
            }
        }
    }
}


QString SslOperation::TakeAddress(QString adress_string)
{
//"https://my.kyivstar.ua"+"<<PostUrl>>"
    int index=adress_string.indexOf("<<");
    int index2=adress_string.indexOf(">>");
    if((index>=0)&&(index2>=0))
    {
        QString prefix=adress_string.left(index);
        QString postfix=adress_string.right(index2);
        return(prefix+(m_hash[adress_string.remove("<<").remove(">>")])+postfix);
    }
    else
        return adress_string;
}
